package com.voodoo.lib_utils;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.Message;
import android.os.SystemClock;

import androidx.annotation.NonNull;

/**
 * Author：voodoo
 * CreateTime：2021/06/16 016 下午 05:16
 * Description：倒计时工具类，修改非主线程中计时崩溃问题
 * 回调都在主线程中，方便更新UI
 */
public abstract class CountDownTimer {

    // 总倒计时的时间 毫秒
    private final long mMillisInFuture;
    // 倒计时间隔时长 毫秒
    private final long mCountdownInterval;
    private long mStopTimeInFuture;

    // 计时器是否已取消
    private boolean mCancelled = false;

    // 设置一个主线程的回调
    private Handler mainLooperHandler;

    /**
     * @param millisInFuture    总计时时间
     * @param countDownInterval 间隔回调
     */
    public CountDownTimer(long millisInFuture, long countDownInterval) {
        mMillisInFuture = millisInFuture;
        mCountdownInterval = countDownInterval;
        if (!isMainThread()) {
            mainLooperHandler = new Handler(Looper.getMainLooper());
            handlerThread = new HandlerThread("CountDownTimerThread");
            handlerThread.start();
            handler = new Handler(handlerThread.getLooper(), mCallback);
        } else {
            handler = new Handler(mCallback);
        }
    }

    private boolean isMainThread() {
        return Looper.getMainLooper().getThread().equals(Thread.currentThread());
    }

    /**
     * 开始启动倒计时
     */
    public synchronized final CountDownTimer start() {
        mCancelled = false;
        if (mMillisInFuture <= 0) {
            onFinish();
            return this;
        }
        mStopTimeInFuture = SystemClock.elapsedRealtime() + mMillisInFuture;
        handler.sendMessage(handler.obtainMessage(MSG));
        return this;
    }

    /**
     * 取消倒计时
     */
    public synchronized final void cancel() {
        mCancelled = true;
        handler.removeMessages(MSG);
    }


    /**
     * 定期触发的回调
     *
     * @param millisUntilFinished 剩余的时间
     */
    public abstract void onTick(long millisUntilFinished);

    /**
     * 计时完成时触发的回调
     */
    public abstract void onFinish();

    private static final int MSG = 1;

    private HandlerThread handlerThread;
    private Handler handler;

    private Handler.Callback mCallback = new Handler.Callback() {

        @Override
        public boolean handleMessage(@NonNull Message message) {
            synchronized (CountDownTimer.this) {
                if (mCancelled) {
                    return true;
                }

                // 剩余时间 剩余时长=总时长-经历的时长
                final long millisLeft = mStopTimeInFuture - SystemClock.elapsedRealtime();

                if (millisLeft <= 0) {
                    // 如果剩余时长 < 0，则回调完成接口
                    if (mainLooperHandler != null) {
                        mainLooperHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                onFinish();
                            }
                        });
                    } else {
                        onFinish();
                    }
                    if (handlerThread != null) {
                        handlerThread.quit();
                    }
                } else if (millisLeft < mCountdownInterval) {
                    // 如果间隔时长 < 指定的时长 则不回调，直接发送下一次Message
                    handler.sendMessageDelayed(handler.obtainMessage(MSG), millisLeft);
                } else {
                    // 否则的话（倒计时未完成并且剩余时长大于等于间隔时长）
                    long lastTickStart = SystemClock.elapsedRealtime();
                    if (mainLooperHandler != null) {
                        mainLooperHandler.post(new Runnable() {
                            @Override
                            public void run() {
                                onTick(millisLeft);
                            }
                        });
                    } else {
                        onTick(millisLeft);
                    }
                    long delay = lastTickStart + mCountdownInterval - SystemClock.elapsedRealtime();
                    while (delay < 0) {
                        delay += mCountdownInterval;
                    }
                    handler.sendMessageDelayed(handler.obtainMessage(MSG), delay);
                }
            }
            return false;
        }
    };

}
